package com.ruoyi.iot.client;

import com.google.common.primitives.Bytes;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Shorts;
import com.ruoyi.iot.utils.CodeUtil;
import com.ruoyi.iot.utils.WarpUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
public class NioClient {

    private final byte FUNC_READ = 0x03;
    private final byte FUNC_WRITE_SINGLE = 0x06;
    private final byte FUNC_WRITE_MULTI = 0x10;

    private final int FUNC_TOTAL_ENERGY_READ = 1;
    private final int FUNC_STATE_RELAYS_READ = 2;
    private final int FUNC_USE_ELECTRICITY_READ = 3;
    private final int FUNC_PROOFREAD_TIME_READ = 4;
    private final int FUNC_TIMER_PERIODS_READ = 5;
    private final int FUNC_MALIGNANT_LOAD_READ = 6;
    private final int FUNC_VIOLATIONS_POWER_READ = 7;
    private final int FUNC_ADDRESS_READ = 8;
    private final int FUNC_REBOOT_TIME_READ = 9;
    private final int FUNC_REAL_STATE_RELAYS_READ = 10;
    private final int FUNC_OVER_CURRENT_THRESHOLD_READ = 11;

    private final int FUNC_RELAYS_CONTROL_WRITE = 100;
    private final int FUNC_TOTAL_ENERGY_WRITE = 101;
    private final int FUNC_STATE_RELAYS_WRITE = 102;
    private final int FUNC_USE_ELECTRICITY_WRITE = 103;
    private final int FUNC_PROOFREAD_TIME_WRITE = 104;
    private final int FUNC_TIMER_PERIODS_WRITE = 105;
    private final int FUNC_MALIGNANT_LOAD_WRITE = 106;
    private final int FUNC_VIOLATIONS_POWER_WRITE = 107;
    private final int FUNC_ADDRESS_WRITE = 108;
    private final int FUNC_REBOOT_TIME_WRITE = 109;
    private final int FUNC_REAL_STATE_RELAYS_WRITE = 110;
    private final int FUNC_OVER_CURRENT_THRESHOLD_WRITE = 111;

    private final byte[] RELAYS_CONTROL = new byte[]{0x02, 0x28};
    private final byte[] RELAYS_CONTROL_OUTAGE = new byte[]{0x33, 0x55};
    private final byte[] RELAYS_CONTROL_ELECTRIFY = new byte[]{(byte) 0x99, 0x66};

    // 校对时间
    private final byte[] PROOFREAD_TIME = new byte[]{0x01, (byte) 0xF4};
    // 恶性负载事件有功功率触发下限
    private final byte[] MALIGNANT_LOAD = new byte[]{0x01, (byte) 0xFE};
    // 启用违规用电的功率下限
    private final byte[] VIOLATIONS_POWER = new byte[]{0x02, 0x0a};
    // 过流门限值
    private final byte[] CURRENT_THRESHOLD = new byte[]{0x02, 0x0c};
    // 有功功率负荷限值
    private final byte[] USE_ELECTRICITY = new byte[]{0x02, 0x01};
    // 定时断、送电表的时段数
    private final byte[] TIMER_PERIODS = new byte[]{0x02, 0x10};
    // 通信地址
    private final byte[] ADDRESS = new byte[]{0x01, (byte) 0xfb};
    // 违规断电后重启时间
    private final byte[] REBOOT_TIME = new byte[]{0x02, 0x07};
    // 继电器状态字
    private final byte[] RELAYS_STATE = new byte[]{0x02, 0x58};

    private final String ip;
    private final Channel channel;
    private final ScheduledExecutorService serialScheduledExecutor;
    private final AtomicInteger atomicAddress;
    private final AtomicReference<PortCallback> atomicPortCallback;
    private final AtomicLong atomicSendTime;
    private final AtomicBoolean needClear;
    private final Map<Byte, Integer> stateMap;
    private ScheduledFuture<?> callBackFailSchedule;

    public NioClient(String ip, Channel channel, ScheduledExecutorService serialScheduledExecutor) {
        this.ip = ip;
        this.channel = channel;
        this.serialScheduledExecutor = serialScheduledExecutor;
        this.stateMap = new ConcurrentHashMap<>();
        this.atomicSendTime = new AtomicLong(System.currentTimeMillis());
        this.atomicPortCallback = new AtomicReference<>(null);
        this.atomicAddress = new AtomicInteger(0xFF);
        this.needClear = new AtomicBoolean(true);
        callBackFailSchedule();
    }

    private void sendData(byte address, byte func, int funcIndex, byte[] sendData, PortCallback portCallback) {
        serialScheduledExecutor.schedule(() -> {
            if (channel.isActive()) {
                byte[] data = Bytes.concat(new byte[]{address, func}, sendData);
                byte[] crc = CodeUtil.getCrc(data);
                byte[] concat = Bytes.concat(data, new byte[]{crc[1], crc[0]});
                channel.writeAndFlush(Unpooled.buffer().writeBytes(concat));
                atomicAddress.set(address);
                atomicSendTime.set(System.currentTimeMillis());
                atomicPortCallback.set(portCallback);
                stateMap.put(address, funcIndex);
                needClear.set(true);
                log.info("send to " + ip + " address:" + address + " index:" + funcIndex + " :" + WarpUtil.bytesToHexString(concat));
            }
        }, (address - 1) * 500 + 10, TimeUnit.MILLISECONDS);
    }

    private void callBackFailSchedule() {
        callBackFailSchedule = serialScheduledExecutor.scheduleWithFixedDelay(() -> {
            if (needClear.get()) {
                log.info("----- callBackFailSchedule " + (System.currentTimeMillis() - atomicSendTime.get()));
                if (System.currentTimeMillis() - atomicSendTime.get() >= 3 * 1000L) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().callBackFail();
                        receiveDone(atomicAddress.byteValue());
                    }
                    needClear.set(false);
                }
            }
        }, 3, 3, TimeUnit.SECONDS);
    }

    public void receiveData(byte[] receivedData) {
        try {
            byte address = receivedData[0];
            log.info("stateMap " + stateMap);
            if (receivedData[1] == FUNC_READ) {
                if (stateMap.get(address) == FUNC_TOTAL_ENERGY_READ) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().readTotalEnergy(
                                BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 3, 4))).multiply(BigDecimal.valueOf(0.01))
                                , BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 7, 4))).multiply(BigDecimal.valueOf(0.01))
                                , BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 11, 4))).multiply(BigDecimal.valueOf(0.01))
                                , BigDecimal.valueOf(Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 15, 2))).multiply(BigDecimal.valueOf(0.1))
                                , BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 17, 4))).multiply(BigDecimal.valueOf(0.001))
                                , BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 21, 4))).multiply(BigDecimal.valueOf(0.0001))
                                , BigDecimal.valueOf(Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 25, 2))).multiply(BigDecimal.valueOf(0.001))
                                , BigDecimal.valueOf(Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 27, 2))).multiply(BigDecimal.valueOf(0.01))
                                , BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 29, 4))).multiply(BigDecimal.valueOf(0.0001))
                                , BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 33, 4))).multiply(BigDecimal.valueOf(0.0001))
                        );
                    }
                } else if (stateMap.get(address) == FUNC_STATE_RELAYS_READ) {
                    if (atomicPortCallback.get() != null) {
                        short state = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 3, 4));
                        atomicPortCallback.get().readMalignantLoadState(state & 0xC000);
                        atomicPortCallback.get().readTotalPowerState(state & 0x3000);
                        atomicPortCallback.get().readPeriodsState(state & 0x0300);
                        atomicPortCallback.get().readSinglePowerState(state & 0x000C);
                    }
                } else if (stateMap.get(address) == FUNC_REAL_STATE_RELAYS_READ) {
                    if (atomicPortCallback.get() != null) {
                        short state = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 3, 4));
                        atomicPortCallback.get().readRelaysRealState(state == 85 ? 0 : 1);
                    }
                } else if (stateMap.get(address) == FUNC_USE_ELECTRICITY_READ) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().readUseElectricity(BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 3, 4))).multiply(BigDecimal.valueOf(0.1)));
                    }
                } else if (stateMap.get(address) == FUNC_MALIGNANT_LOAD_READ) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().readMalignantLoadLimit(BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 3, 4))).multiply(BigDecimal.valueOf(0.1)));
                    }
                } else if (stateMap.get(address) == FUNC_REBOOT_TIME_READ) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().readRebootTime(Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 3, 2)));
                    }
                } else if (stateMap.get(address) == FUNC_VIOLATIONS_POWER_READ) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().readViolationsPower(BigDecimal.valueOf(Ints.fromByteArray(WarpUtil.subBytes(receivedData, 3, 4))).multiply(BigDecimal.valueOf(0.1)));
                    }
                } else if (stateMap.get(address) == FUNC_ADDRESS_READ) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().readAddress(Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 3, 2)));
                    }
                } else if (stateMap.get(address) == FUNC_PROOFREAD_TIME_READ) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().readProofreadTime(WarpUtil.bytesToInteger(WarpUtil.subBytes(receivedData, 3, 2)),
                                WarpUtil.bytesToInteger(WarpUtil.subBytes(receivedData, 5, 2)),
                                WarpUtil.bytesToInteger(WarpUtil.subBytes(receivedData, 7, 2)),
                                WarpUtil.bytesToInteger(WarpUtil.subBytes(receivedData, 9, 2)),
                                WarpUtil.bytesToInteger(WarpUtil.subBytes(receivedData, 11, 2)),
                                WarpUtil.bytesToInteger(WarpUtil.subBytes(receivedData, 13, 2)),
                                WarpUtil.bytesToInteger(WarpUtil.subBytes(receivedData, 15, 2))
                        );
                    }
                } else if (stateMap.get(address) == FUNC_TIMER_PERIODS_READ) {
                    if (atomicPortCallback.get() != null) {
                        int count = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 3, 2));
                        if (count > 0) {
                            short period1Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 5, 2));
                            String period1Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 7, 2));
                            short period2Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 9, 2));
                            String period2Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 11, 2));
                            short period3Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 13, 2));
                            String period3Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 15, 2));
                            short period4Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 17, 2));
                            String period4Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 19, 2));
                            short period5Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 21, 2));
                            String period5Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 23, 2));
                            short period6Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 25, 2));
                            String period6Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 27, 2));
                            short period7Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 29, 2));
                            String period7Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 31, 2));
                            short period8Status = Shorts.fromByteArray(WarpUtil.subBytes(receivedData, 33, 2));
                            String period8Time = WarpUtil.periodsBytesToInteger(WarpUtil.subBytes(receivedData, 35, 2));
//                            if (period1Status == 1) {
//                                atomicPortCallback.get().readPeriods(period1Time, period2Time);
//                            } else {
//                                atomicPortCallback.get().readPeriods(period2Time, period1Time);
//                            }
                            atomicPortCallback.get().readPeriods(period1Status, period1Time, period2Status, period2Time);
                        } else {
                            atomicPortCallback.get().readPeriods();
                        }
                    }
                }
                receiveDone(address);
            } else if (receivedData[1] == FUNC_WRITE_SINGLE || receivedData[1] == FUNC_WRITE_MULTI) {
                if (receivedData[2] == RELAYS_CONTROL[0] && receivedData[3] == RELAYS_CONTROL[1]) {
                    if (receivedData[4] == RELAYS_CONTROL_OUTAGE[0] && receivedData[5] == RELAYS_CONTROL_OUTAGE[1]) {
                        if (atomicPortCallback.get() != null) {
                            atomicPortCallback.get().writeOutage();
                        }
                    } else if (receivedData[4] == RELAYS_CONTROL_ELECTRIFY[0] && receivedData[5] == RELAYS_CONTROL_ELECTRIFY[1]) {
                        if (atomicPortCallback.get() != null) {
                            atomicPortCallback.get().writeElectrify();
                        }
                    }
                } else if (receivedData[2] == MALIGNANT_LOAD[0] && receivedData[3] == MALIGNANT_LOAD[1]) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().writeMalignantLoadLimit();
                    }
                } else if (receivedData[2] == USE_ELECTRICITY[0] && receivedData[3] == USE_ELECTRICITY[1]) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().writeUseElectricity();
                    }
                } else if (receivedData[2] == PROOFREAD_TIME[0] && receivedData[3] == PROOFREAD_TIME[1]) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().writeProofreadTime();
                    }
                } else if (receivedData[2] == TIMER_PERIODS[0] && receivedData[3] == TIMER_PERIODS[1]) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().writePeriods(receivedData[1] == 0x06 ? 0 : 1);
                    }
                } else if (receivedData[2] == ADDRESS[0] && receivedData[3] == ADDRESS[1]) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().writeAddress();
                    }
                } else if (receivedData[2] == REBOOT_TIME[0] && receivedData[3] == REBOOT_TIME[1]) {
                    if (atomicPortCallback.get() != null) {
                        atomicPortCallback.get().writeRebootTime();
                    }
                }
                receiveDone(address);
            } else {
                receiveDone(address);
                log.error("功能码错误 " + receivedData[1]);
            }
        } catch (Exception e) {
            log.error("电表通讯异常", e);
        }
    }

    private void receiveDone(byte address) {
        stateMap.remove(address);
        atomicPortCallback.set(null);
        needClear.set(false);
    }

    /*******************读*************************/
    // 当前组合有功总电能 1
    public void readTotalEnergy(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_TOTAL_ENERGY_READ, new byte[]{0x00, 0x00, 0x00, 0x10}, portCallback);
    }

    // 继电器状态字
    public void readRelaysState(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_STATE_RELAYS_READ, new byte[]{0x02, 0x58, 0x00, 0x01}, portCallback);
    }

    // 继电器实际状态 0xaa:合上，0x55:断开
    public void readRelaysRealState(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_REAL_STATE_RELAYS_READ, new byte[]{0x02, 0x75, 0x00, 0x01}, portCallback);
    }

    // 恶性负载事件有功功率触发下限
    // 0.0001kW (设为0时,则不启用该功能)
    public void readMalignantLoadLimit(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_MALIGNANT_LOAD_READ, Bytes.concat(MALIGNANT_LOAD, new byte[]{0x00, 0x02}), portCallback);
    }

    // 违规断电后重启时间 0x0207
    // 范围 1~65535 秒（设为0，则不能重启用电）
    public void readRebootTime(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_REBOOT_TIME_READ, Bytes.concat(REBOOT_TIME, new byte[]{0x00, 0x02}), portCallback);
    }

    // 启用违规用电的功率下限
    // 0.0001kW,(不能设置小于0.03kW的值)
    public void readViolationsPower(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_VIOLATIONS_POWER_READ, Bytes.concat(VIOLATIONS_POWER, new byte[]{0x00, 0x02}), portCallback);
    }

    // 过流门限值
    // 0.0001A（设 0 则不启用该功能）
    public void readOverCurrentThreshold(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_OVER_CURRENT_THRESHOLD_READ, Bytes.concat(CURRENT_THRESHOLD, new byte[]{0x00, 0x02}), portCallback);
    }

    // 有功功率负荷限值 1
    // 0.0001kW,(设为0 时,则不启用该功能)
    public void readUseElectricity(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_USE_ELECTRICITY_READ, Bytes.concat(USE_ELECTRICITY, new byte[]{0x00, 0x02}), portCallback);
    }

    // 校对时间 1
    public void readProofreadTime(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_PROOFREAD_TIME_READ, Bytes.concat(PROOFREAD_TIME, new byte[]{0x00, 0x07}), portCallback);
    }

    // 定时断送电 1
    public void readPeriods(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_TIMER_PERIODS_READ, Bytes.concat(TIMER_PERIODS, new byte[]{0x00, 0x11}), portCallback);
    }

    // 通信地址
    public void readAddress(byte address, PortCallback portCallback) {
        sendData(address, FUNC_READ, FUNC_ADDRESS_READ, Bytes.concat(ADDRESS, new byte[]{0x00, 0x11}), portCallback);
    }


    /*******************写*************************/
    // 继电器控制 断电 1
    public void writeOutage(byte address, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_SINGLE, FUNC_RELAYS_CONTROL_WRITE, Bytes.concat(RELAYS_CONTROL, RELAYS_CONTROL_OUTAGE), portCallback);
    }

    // 继电器控制 通电 1
    public void writeElectrify(byte address, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_SINGLE, FUNC_RELAYS_CONTROL_WRITE, Bytes.concat(RELAYS_CONTROL, RELAYS_CONTROL_ELECTRIFY), portCallback);
    }

    // 校对时间 1
    public void writeProofreadTime(byte address, PortCallback portCallback) {
        LocalDateTime now = LocalDateTime.now();
        log.info("now " + now);
        int year = now.getYear();
        int month = now.getMonth().getValue();
        int day = now.getDayOfMonth();
        int week = now.getDayOfWeek().getValue();
        int hour = now.getHour();
        int minute = now.getMinute();
        int second = now.getSecond();

        sendData(address, FUNC_WRITE_MULTI, FUNC_PROOFREAD_TIME_WRITE, Bytes.concat(PROOFREAD_TIME, new byte[]{0x00, 0x07, 0x0E}, WarpUtil.integerToBytes(second),
                WarpUtil.integerToBytes(minute), WarpUtil.integerToBytes(hour),
                WarpUtil.integerToBytes(week), WarpUtil.integerToBytes(day), WarpUtil.integerToBytes(month), WarpUtil.integerToBytes(year)
        ), portCallback);
    }

    // 恶性负载事件有功功率触发下限
    // 0.0001kW (设为0时,则不启用该功能)
    public void writeMalignantLoadLimit(byte address, BigDecimal w, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_MULTI, FUNC_MALIGNANT_LOAD_WRITE, Bytes.concat(MALIGNANT_LOAD, new byte[]{0x00, 0x02, 0x04}, Ints.toByteArray(w.multiply(BigDecimal.valueOf(10)).intValue())), portCallback);
    }

    // 启用违规用电的功率下限
    // 0.0001kW,(不能设置小于0.03kW的值)
    public void writeViolationsPower(byte address, BigDecimal w, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_MULTI, FUNC_VIOLATIONS_POWER_WRITE, Bytes.concat(VIOLATIONS_POWER, new byte[]{0x00, 0x02, 0x04}, Ints.toByteArray(w.multiply(BigDecimal.valueOf(10)).intValue())), portCallback);
    }

    // 过流门限值
    // 0.0001A（设 0 则不启用该功能）
    public void writeOverCurrentThreshold(byte address, BigDecimal a, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_MULTI, FUNC_OVER_CURRENT_THRESHOLD_WRITE, Bytes.concat(CURRENT_THRESHOLD, new byte[]{0x00, 0x02, 0x04}, Ints.toByteArray(a.multiply(BigDecimal.valueOf(10)).intValue())), portCallback);
    }

    // 有功功率负荷限值 1
    // 0.0001kW,(设为0 时,则不启用该功能)
    public void writeUseElectricity(byte address, BigDecimal w, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_MULTI, FUNC_USE_ELECTRICITY_WRITE, Bytes.concat(USE_ELECTRICITY, new byte[]{0x00, 0x02, 0x04}, Ints.toByteArray(w.multiply(BigDecimal.valueOf(10)).intValue())), portCallback);
    }

    // 定时断送电 1
    public boolean writePeriods(byte address, short state1, String time1, short state2, String time2, PortCallback portCallback) {
//        if (closeTime.length() != 5 || !closeTime.contains(":") || openTime.length() != 5 || !openTime.contains(":")) {
//            log.error("定时断送电时间格式不正确 " + closeTime + " " + openTime);
//            return false;
//        }
//        int closeHour = Integer.parseInt(closeTime.substring(0, 2));
//        int openHour = Integer.parseInt(openTime.substring(0, 2));
//
//        int closeMinute = Integer.parseInt(closeTime.substring(3, 5));
//        int openMinute = Integer.parseInt(openTime.substring(3, 5));
//
//        log.info("======= closeHour:" + closeHour + " openHour:" + openHour);
//        log.info("======= closeMinute:" + closeMinute + " openMinute:" + openMinute);

//        if (openHour < closeHour || (openHour == closeHour && openMinute < closeMinute)) {
//            sendData(address, FUNC_WRITE_MULTI, FUNC_TIMER_PERIODS_WRITE, Bytes.concat(TIMER_PERIODS, new byte[]{0x00, 0x05, 0x0A}, new byte[]{0x00, 0x02},
//                    new byte[]{0x00, 0x02}, WarpUtil.periodsIntegerToBytes(openTime),
//                    new byte[]{0x00, 0x01}, WarpUtil.periodsIntegerToBytes(closeTime)
//            ), portCallback);
//        } else {
//            sendData(address, FUNC_WRITE_MULTI, FUNC_TIMER_PERIODS_WRITE, Bytes.concat(TIMER_PERIODS, new byte[]{0x00, 0x05, 0x0A}, new byte[]{0x00, 0x02},
//                            new byte[]{0x00, 0x01}, WarpUtil.periodsIntegerToBytes(closeTime),
//                            new byte[]{0x00, 0x02}, WarpUtil.periodsIntegerToBytes(openTime)),
//                    portCallback);
//        }

        sendData(address, FUNC_WRITE_MULTI, FUNC_TIMER_PERIODS_WRITE, Bytes.concat(TIMER_PERIODS, new byte[]{0x00, 0x05, 0x0A}, new byte[]{0x00, 0x02},
                Shorts.toByteArray(state1), WarpUtil.periodsIntegerToBytes(time1),
                Shorts.toByteArray(state2), WarpUtil.periodsIntegerToBytes(time2)
        ), portCallback);

        return true;
    }

    public void writePeriods(byte address, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_SINGLE, FUNC_TIMER_PERIODS_WRITE, Bytes.concat(TIMER_PERIODS, new byte[]{0x00, 0x00}), portCallback);
    }

    // 通信地址
    public void writeAddress(byte address, short addr, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_SINGLE, FUNC_ADDRESS_WRITE, Bytes.concat(ADDRESS, Shorts.toByteArray(addr)), portCallback);
    }

    // 违规断电后重启时间 0x0207
    // 范围 1~65535 秒（设为0，则不能重启用电）
    public void writeRebootTime(byte address, short time, PortCallback portCallback) {
        sendData(address, FUNC_WRITE_SINGLE, FUNC_REBOOT_TIME_WRITE, Bytes.concat(REBOOT_TIME, Shorts.toByteArray(time)), portCallback);
    }

    public interface PortCallback {

        /**
         * 只读区域
         *
         * @param totalW    当前组合有功总电能 0.01kWh
         * @param positiveW 当前正向有功总电能 0.01kWh
         * @param negativeW 当前反向有功总电能 0.01kWh
         * @param v         电压 0.1V
         * @param a         电流 0.001A
         * @param w         有功功率 0.0001kW
         * @param factor    功率因数 0.001
         * @param rate      频率 0.01Hz
         * @param reactiveW 无功功率 0.0001kvar
         * @param apparentW 视在功率 0.0001kVA
         */
        void readTotalEnergy(BigDecimal totalW, BigDecimal positiveW, BigDecimal negativeW, BigDecimal v, BigDecimal a, BigDecimal w,
                             BigDecimal factor, BigDecimal rate, BigDecimal reactiveW, BigDecimal apparentW);

        // 断电
        void writeOutage();

        // 通电
        void writeElectrify();

        // 继电器状态字 有恶性负载标志
        void readMalignantLoadState(int state);

        // 继电器状态字 总功率超限标志
        void readTotalPowerState(int state);

        // 继电器状态字 定时断电标志
        void readPeriodsState(int state);

        // 继电器状态字 单次功率超限标志
        void readSinglePowerState(int state);

        // 校对时间
        void writeProofreadTime();

        void readProofreadTime(int second, int minute, int hour, int week, int day, int month, int year);

        // 恶性负载事件有功功率触发下限
        // 0.0001kW (设为0时,则不启用该功能)
        void writeMalignantLoadLimit();

        void readMalignantLoadLimit(BigDecimal w);

        // 启用违规用电的功率下限
        // 0.0001kW,(不能设置小于0.03kW的值)
        void writeViolationsPower();

        void readViolationsPower(BigDecimal w);

        // 有功功率负荷限值
        // 0.0001kW,(设为0 时,则不启用该功能)
        void writeUseElectricity();

        void readUseElectricity(BigDecimal w);

        // 定时断送电 时间格式：HHmm runType 0无时控 1有时控
        void writePeriods(int runType);

        void readPeriods(int state1, String time1, int state2, String time2);

        void readPeriods();

        // 通信地址
        void writeAddress();

        void readAddress(short address);

        // 违规断电后重启时间
        void writeRebootTime();

        void readRebootTime(short time);

        // 继电器实际状态 0 关闭 1 开
        void readRelaysRealState(int state);

        // 无回复
        void callBackFail();
    }

    @Override
    public String toString() {
        return ip + " " + channel.id().asShortText();
    }

    public void close() {
        if (callBackFailSchedule != null) {
            callBackFailSchedule.cancel(true);
            callBackFailSchedule = null;
        }
    }
}
